package com.hqd.jjvm.classpath;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;

import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipInputStream;

/**
 * 加载路径下的zip文件
 *
 * @author hqd
 */
public class ZipClassResource extends AbstractClassResource {


    public ZipClassResource(String rootPath, String fileSuffix) {
        super(rootPath, fileSuffix);
    }

    public ZipClassResource(String rootPath) {
        this(rootPath, ZIP_FILE_SUFFIX);
    }

    @Override
    protected void init() {
        super.init();
        for (File file : getClassFileList()) {
            ZipFile zipFile = null;
            try {
                zipFile = new ZipFile(file.getPath());
                Enumeration<ZipEntry> entries = zipFile.getEntries();
                while (entries.hasMoreElements()) {
                    ZipEntry zipEntry = entries.nextElement();
                    if (!zipEntry.isDirectory() && zipEntry.getName().endsWith(CLASS_FILE_SUFFIX)) {
                        ClassFileInfo classFileInfo = new ClassFileInfo();
                        classFileInfo.setClassPath(zipEntry.getName());
                        classFileInfo.setFileName(file.getName());
                        classFileInfo.setPath(file.getPath());
                        getClassHexMap().put(zipEntry.toString(), classFileInfo);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                if (zipFile != null) {
                    try {
                        zipFile.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 获取zip包中class文件的二进制流
     *
     * @param classNamePath
     * @return
     * @throws IOException
     */
    protected ClassFileInfo parseClassInZip(String classNamePath) throws IOException {
        ClassFileInfo classFileInfo = getClassHexMap().get(classNamePath);
        if (classFileInfo != null) {
            ZipFile zf = new ZipFile(classFileInfo.getPath());
            InputStream in = null;
            ZipInputStream zin = null;
            try {
                getClassHexMap().get(classNamePath).setByteCode(super.readClassFileToStr(zf.getInputStream(zf.getEntry(classNamePath))));
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                if (in != null) {
                    in.close();
                }
                if (zf != null) {
                    zf.close();
                }
                if (zin != null) {
                    zin.closeEntry();
                }
            }
        }
        return classFileInfo;
    }

    protected void parseAllClassInZip(File file) throws IOException {
        ZipFile zf = new ZipFile(file.getPath());
        InputStream in = null;
        ZipInputStream zin = null;
        try {
            in = new BufferedInputStream(new FileInputStream(file.getPath()));
            zin = new ZipInputStream(in);
            Enumeration<ZipEntry> entries = zf.getEntries();
            while (entries.hasMoreElements()) {
                ZipEntry zipEntry = entries.nextElement();
                if (zipEntry.toString().endsWith(CLASS_FILE_SUFFIX)) {
                    getClassHexMap().get(zipEntry.getName()).setByteCode(super.readClassFileToStr(zf.getInputStream(zipEntry)));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (in != null) {
                in.close();
            }
            if (zf != null) {
                zf.close();
            }
            if (zin != null) {
                zin.closeEntry();
            }
        }
    }

    @Override
    public List<ClassFileInfo> readAllClass() throws IOException {
        parseAllClassInZip(new File(getRootPath()));
        return new ArrayList<>(getClassHexMap().values());
    }


    @Override
    public ClassFileInfo readClass(String classNamePath) throws IOException {

        return parseClassInZip(classNamePath.intern());
    }
}
